package org.jsets.fastboot.oss.controller;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jsets.fastboot.eventbus.EventBus;
import org.jsets.fastboot.exception.BadRequestException;
import org.jsets.fastboot.model.BaseResp;
import org.jsets.fastboot.model.DataResp;
import org.jsets.fastboot.model.ListResp;
import org.jsets.fastboot.oss.config.ObjectStorageProperties;
import org.jsets.fastboot.oss.model.constant.Buckets;
import org.jsets.fastboot.oss.model.constant.Periods;
import org.jsets.fastboot.oss.model.constant.StatusTypes;
import org.jsets.fastboot.oss.model.dto.FileMetaQuery;
import org.jsets.fastboot.oss.model.entity.Bucket;
import org.jsets.fastboot.oss.model.entity.FileMeta;
import org.jsets.fastboot.oss.model.entity.Outside;
import org.jsets.fastboot.oss.model.event.FileAccessEvent;
import org.jsets.fastboot.oss.model.event.OutsideDisabledEvent;
import org.jsets.fastboot.oss.service.IBucketService;
import org.jsets.fastboot.oss.service.IFileMetaService;
import org.jsets.fastboot.oss.service.IOutsideService;
import org.jsets.fastboot.util.FileUtils;
import org.jsets.fastboot.util.IoUtils;
import org.jsets.fastboot.util.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.util.FileCopyUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import cn.hutool.core.util.ZipUtil;
import org.apache.tika.Tika;
/**
 * @author wangjie
 */
@RestController
@RequestMapping("/oss/files")
@Api(tags = "文件")
public class FileMetaController {

	@Autowired
	private ObjectStorageProperties properties;
	@Autowired
	private IFileMetaService service;
	@Autowired
	private IBucketService bucketService;
	@Autowired
	private IOutsideService outsideService;

	@PostMapping(value = "/{bucket}/upload")
	@ApiOperation("上传附件")
	public DataResp<String> upload(
			@RequestParam("file") MultipartFile file, 
			@PathVariable("bucket") String bucketName) {
		
		if (null == bucketName || "".equals(bucketName)) {
			return DataResp.fail("文件桶不能为空");
		}
		
		if (null == file || file.getSize() == 0) {
			return DataResp.fail("文件不能为空");
		}
		
		Bucket bucket = this.bucketService.getByName(bucketName);
		if (Objects.isNull(bucket)) {
			throw new BadRequestException("找不到名称为["+bucketName+"]的桶");
		}
		
		String fileName = file.getOriginalFilename();
		String extension = FileUtils.getExtension(file.getOriginalFilename());

		if (StringUtils.notEmpty(bucket.getExcludeType())) {
			List<String> excludes = StringUtils.split(bucket.getExcludeType());
			for (String exclude : excludes) {
				if (exclude.equalsIgnoreCase(extension)) {
					throw new BadRequestException("不支持的文件类型");
				}
			}
		}
		
		FileMeta meta = new FileMeta();
		meta.setContentType(file.getContentType());
		meta.setStorageId(bucket.getStorageId());
		meta.setBucketName(bucketName);
		meta.setExtension(extension);
		meta.setName(fileName);
		meta.setLength(file.getSize());
		meta.setUploadTime(new Date());
		meta.setLastAccessTime(new Date());

		InputStream is = null;
		try {
			is = file.getInputStream();
			Long id = this.service.insertFile(meta, is);
			if (Objects.isNull(id)) {
				throw new RuntimeException("文件上传失败");
			}
			//if (bucket.getNoticeable()) {
				//发送消息
			//}
			DataResp<String> respond = DataResp.ok();
			respond.setData(String.valueOf(id));
			return respond;
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage(), e);
		} finally {
			IoUtils.closeQuietly(is);
		}
	}

	@PostMapping(value = "/upload")
	@ApiOperation("上传附件")
	public DataResp<String> upload(@RequestParam("file") MultipartFile file, HttpServletRequest request) {
		if (null == file || file.getSize() == 0) {
			return DataResp.fail("文件不能为空");
		}
		
		String bucketName = request.getParameter("bucketName");
		if (StringUtils.isEmpty(bucketName)) {
			bucketName = Buckets.DEFAULT;
		}
		Bucket bucket = this.bucketService.getByName(bucketName);
		if (Objects.isNull(bucket)) {
			throw new BadRequestException("找不到名称为["+bucketName+"]的桶");
		}
		
		String fileName = file.getOriginalFilename();
		String extension = FileUtils.getExtension(file.getOriginalFilename());
		String setFileName = request.getParameter("fileName");
		String setExtension = request.getParameter("extension");
		if (StringUtils.notEmpty(setFileName)) {
			fileName = setFileName;
		}
		if (StringUtils.notEmpty(setExtension)) {
			extension = setExtension;
		}
		
		if (StringUtils.notEmpty(bucket.getExcludeType())) {
			List<String> excludes = StringUtils.split(bucket.getExcludeType());
			for (String exclude : excludes) {
				if (exclude.equalsIgnoreCase(extension)) {
					throw new BadRequestException("不支持的文件类型");
				}
			}
		}
		
		FileMeta meta = new FileMeta();
		meta.setContentType(file.getContentType());
		meta.setStorageId(bucket.getStorageId());
		meta.setBucketName(bucketName);
		meta.setExtension(extension);
		meta.setName(fileName);
		meta.setLength(file.getSize());
		meta.setUploadTime(new Date());
		meta.setLastAccessTime(new Date());
		meta.setAppName(request.getParameter("appName"));
		meta.setBizIdentifier(request.getParameter("bizIdentifier"));
		meta.setBizType(request.getParameter("bizType"));
		meta.setRemark(request.getParameter("remark"));
		String setId = request.getParameter("id");
		if (StringUtils.notEmpty(setId)) {
			meta.setId(Long.valueOf(setId));
		}

		InputStream is = null;
		try {
			is = file.getInputStream();
			Long id = this.service.insertFile(meta, is);
			if (Objects.isNull(id)) {
				throw new RuntimeException("文件上传失败");
			}
			if (bucket.getNoticeable()) {
				//发送消息
			}
			DataResp<String> respond = DataResp.ok();
			respond.setData(String.valueOf(id));
			return respond;
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage(), e);
		} finally {
			IoUtils.closeQuietly(is);
		}
	}
	
	@PostMapping(value = "/uploads")
	@ApiOperation("上传附件")
	public DataResp<String> uploads(@RequestParam("files") MultipartFile[] files, HttpServletRequest request) {
		if (null == files || files.length == 0) {
			return DataResp.fail("文件不能为空");
		}
		
		String bucketName = request.getParameter("bucketName");
		if (StringUtils.isEmpty(bucketName)) {
			bucketName = Buckets.DEFAULT;
		}
		Bucket bucket = this.bucketService.getByName(bucketName);
		if (Objects.isNull(bucket)) {
			throw new BadRequestException("找不到名称为["+bucketName+"]的桶");
		}
		List<String> excludes = StringUtils.split(bucket.getExcludeType());
		List<String> updateds = Lists.newArrayList();
		
		for(MultipartFile file:files) {
			String fileName = file.getOriginalFilename();
			String extension = FileUtils.getExtension(file.getOriginalFilename());
			String setFileName = request.getParameter("fileName");
			String setExtension = request.getParameter("extension");
			if (StringUtils.notEmpty(setFileName)) {
				fileName = setFileName;
			}
			if (StringUtils.notEmpty(setExtension)) {
				extension = setExtension;
			}
			for (String exclude : excludes) {
				if (exclude.equalsIgnoreCase(extension)) {
					throw new BadRequestException("不支持的文件类型");
				}
			}
			
			
			System.out.println("-----------------111111111");
			System.out.println(file.getContentType());
			System.out.println("-----------------22222222");
			
			System.out.println("-----------------333333333333");
			try {
				System.out.println(new Tika().detect(file.getInputStream()));
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			System.out.println("-----------------44444444444");
		
			
			
			FileMeta meta = new FileMeta();
			meta.setStorageId(bucket.getStorageId());
			meta.setBucketName(bucketName);
			meta.setExtension(extension);
			meta.setName(fileName);
			meta.setLength(file.getSize());
			meta.setUploadTime(new Date());
			meta.setLastAccessTime(new Date());
			meta.setAppName(request.getParameter("appName"));
			meta.setBizIdentifier(request.getParameter("bizIdentifier"));
			meta.setBizType(request.getParameter("bizType"));
			meta.setRemark(request.getParameter("remark"));
			String setId = request.getParameter("id");
			if (StringUtils.notEmpty(setId)) {
				meta.setId(Long.valueOf(setId));
			}
			System.out.println("-----------------5555555555555");
			InputStream is = null;
			try {
				is = file.getInputStream();
				Long id = this.service.insertFile(meta, is);
				if (Objects.isNull(id)) {
					throw new RuntimeException("文件上传失败");
				}
				System.out.println("-----------------6666666666666");
//				if (bucket.getNoticeable()) {
//					//发送消息
//				}
				System.out.println("-----------------eeeeeeeee");
				updateds.add(String.valueOf(id));
				System.out.println("-----------------7777777777777");
			} catch (Exception e) {
				throw new RuntimeException(e.getMessage(), e);
			} finally {
				IoUtils.closeQuietly(is);
			}
		}
		System.out.println("-----------------8888888888888");
		DataResp<String> respond = DataResp.ok();
		respond.setData(StringUtils.join(updateds));
		return respond;
	}
	
	@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
	protected ListResp<FileMeta> list(
			@RequestParam(name = "page_no", required = false) Integer pageNo,
			@RequestParam(name = "page_size", required = false) Integer pageSize,
			@RequestParam(name = "name", required = false) String name,
			@RequestParam(name = "bucket_name", required = false) String bucketName,
			@RequestParam(name = "app_name", required = false) String appName,
			@RequestParam(name = "biz_identifier", required = false) String bizIdentifier,
			@RequestParam(name = "biz_type", required = false) String bizType,
			@RequestParam(name = "extension", required = false) String extension,
			@RequestParam(name = "update_time_start", required = false) String uploadTimeStart,
			@RequestParam(name = "update_time_end", required = false) String uploadTimeEnd) {

		FileMetaQuery criteria = new FileMetaQuery()
				.setCurrent(pageNo)
				.setSize(pageSize)
				.setName(appName)
				.setBucketName(bucketName)
				.setAppName(appName)
				.setBizIdentifier(bizIdentifier)
				.setBizType(bizType)
				.setExtension(extension)
				.setUploadTimeStart(uploadTimeStart)
				.setUploadTimeEnd(uploadTimeEnd);
		
		ListResp<FileMeta> respond = ListResp.ok();
		if(criteria.isPageNecessary()) {
			Page<FileMeta> result = this.service.selectPage(criteria);
			respond.setData(result.getRecords());
			respond.setTotalCount(result.getTotal());
			return respond;
		}
		respond.setData(this.service.selectList(criteria));
		return respond;
	}

	@DeleteMapping(value="/{id}" ,produces = MediaType.APPLICATION_JSON_VALUE)
	@ApiOperation("删除文件")
	@ApiImplicitParams({
		@ApiImplicitParam(name="id", value="主键", required=true, dataType = "Long", dataTypeClass = Long.class, paramType="path")
	})
	public BaseResp delete(@PathVariable("id") Long id) {
		Objects.requireNonNull(id, "主键[id]不能为空");
		this.service.deleteFile(id);
		return BaseResp.ok();
	}
	
	@DeleteMapping(value = "/batch_delete", produces = MediaType.APPLICATION_JSON_VALUE)
	@ApiOperation("批量删除文件")
	@ApiImplicitParams({
		@ApiImplicitParam(name = "ids", value = "主键", required = true, dataType = "String", dataTypeClass = String.class, paramType = "query") 
	})
	public BaseResp batchDelete(@RequestParam("ids") String ids) {
		Objects.requireNonNull(ids, "主键列表[ids]不能为空");
		List<Long> idList = Lists.newArrayList();
		StringUtils.split(ids).forEach(id -> idList.add(Long.valueOf(id)));
		this.service.batchDeleteFile(idList);
		return BaseResp.ok();
	}

	@RequestMapping("/download/{id}")
	public void download(@PathVariable("id") Long id, HttpServletResponse response) {
		FileMeta meta = this.service.getById(id);
		if (null == meta) {
			throw new RuntimeException("对象不存在");
		}
		
		InputStream inputStream = this.service.getFileInputStream(meta);
		response.setHeader("content-type", "application/octet-stream");
		response.setContentType("application/octet-stream");
		try {
			response.setHeader("Content-Disposition","attachment;filename=" + new String(meta.getName().getBytes("UTF-8"), "ISO-8859-1"));
			OutputStream output = response.getOutputStream();
			FileCopyUtils.copy(inputStream, output);
			output.flush();
		} catch (IOException e) {
			throw new RuntimeException(e.getMessage(), e);
		} finally {
			EventBus.asyncPost(new FileAccessEvent(id));
			IoUtils.closeQuietly(inputStream);
			inputStream = null;
		}
	}
	
	@RequestMapping("/outside/{id}")
	public void outsideDownload(@PathVariable("id") String id, HttpServletResponse response) {
		Long outsideId = Long.valueOf(StringUtils.URLBase64Decode(id));
		Outside outside = this.outsideService.getById(outsideId);
		if(Objects.isNull(outside)) {
			throw new RuntimeException("无效的链接");
		}

		if (StatusTypes.DISABLE==outside.getStatus()) {
			throw new RuntimeException("链接已失效");
		}
		
		if (Objects.nonNull(outside.getDueTime()) && outside.getDueTime().before(new Date())) {
			EventBus.asyncPost(new OutsideDisabledEvent(outsideId));
			throw new RuntimeException("链接已失效");
		}

		FileMeta meta = this.service.getById(outside.getFileId());
		if (null == meta) {
			throw new RuntimeException("无效的链接");
		}
		
		if (Periods.ONCE == outside.getPeriod()) {
			EventBus.asyncPost(new OutsideDisabledEvent(outsideId));
		}

		EventBus.asyncPost(new FileAccessEvent(outside.getFileId()));
		this.writeToOutputStream(this.service.getFileInputStream(meta), meta.getName(), response);
	}
	
	@RequestMapping("/download_batch")
	public void download(
			@RequestParam(name = "ids", required = false) String ids,
			@RequestParam(name = "biz_identifier", required = false) String bizIdentifier,
			@RequestParam(name = "biz_type", required = false) String bizType,
			@RequestParam(name = "package_name", required = false) String packageName, 
			HttpServletResponse response) {
		
		List<InputStream> inputStreams = Lists.newArrayList();
		List<String> names = Lists.newArrayList();
		if(StringUtils.notEmpty(ids)) {
			List<String> idList = StringUtils.split(ids);
			for(String id : idList) {
				FileMeta metadata = this.service.getById(id);
				if (Objects.isNull(metadata)) {
					throw new RuntimeException("无效的文件ID");
				}
				inputStreams.add(this.service.getFileInputStream(metadata));
				names.add(metadata.getName());
			}
		} else {
			FileMetaQuery criteria = new FileMetaQuery()
					.setBizIdentifier(bizIdentifier)
					.setBizType(bizType);
			List<FileMeta> metas  =this.service.selectList(criteria);
			for(FileMeta meta : metas) {
				inputStreams.add(this.service.getFileInputStream(meta));
				names.add(meta.getName());
				EventBus.asyncPost(new FileAccessEvent(meta.getId()));
			}
		}
		
		String zipName = packageName;
		if (StringUtils.isEmpty(zipName)) {
			zipName = StringUtils.getUUID();
		}
		zipName += ".zip";
		try {
			response.setHeader("content-type", "application/octet-stream");
			response.setContentType("application/octet-stream");
			response.setHeader("Content-Disposition","attachment;filename=" + new String(zipName.getBytes("UTF-8"), "ISO-8859-1"));
			OutputStream output = response.getOutputStream();
			String[] srcFileNames = new String[names.size()];
			InputStream[] srcFiles = new InputStream[inputStreams.size()];
			for(int i = 0; i < inputStreams.size(); i++) {		
				srcFileNames[i] = names.get(i);
				srcFiles[i] = inputStreams.get(i);
			}
			ZipUtil.zip(output, srcFileNames, srcFiles);
			output.flush();
		} catch (IOException e) {
			throw new RuntimeException(e.getMessage(), e);
		}
	}
	
	@GetMapping(value = "/preview_url/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
	public DataResp<String> getPreviewUrl(@PathVariable("id") String id) {
		FileMeta meta = this.service.getById(id);
		Outside outside = new Outside();
		outside.setFileId(meta.getId());
		outside.setRequireAuth(false);
		outside.setPeriod(Periods.ONCE);
		String outsidePath = this.outsideService.insertOutside(outside);
		StringBuilder sb = new StringBuilder();
		sb.append(this.properties.getPreviewHost());
		sb.append("/onlinePreview"); 
		sb.append("?url=");
		sb.append(StringUtils.URLBase64Encode(outsidePath+"?fullfilename="+meta.getName()));
		
		DataResp<String> respond = DataResp.ok();
		respond.setData(sb.toString());
		return respond;
	}

	private void writeToOutputStream(InputStream inputStream, String fileName, HttpServletResponse response) {
		response.setHeader("content-type", "application/octet-stream");
		response.setContentType("application/octet-stream");
		try {
			response.setHeader("Content-Disposition","attachment;filename=" + new String(fileName.getBytes(StringUtils.UTF8), StringUtils.ISO88591));
			OutputStream output = response.getOutputStream();
			FileCopyUtils.copy(inputStream, output);
			output.flush();
		} catch (IOException e) {
			throw new RuntimeException(e.getMessage(), e);
		} finally {
			IoUtils.closeQuietly(inputStream);
			inputStream = null;
		}
	}

}